/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.internal.datasource;
/**
* This class models a datasource factory.The datasource factory has funtions to create 3 types of
* datasources. 1) Basic datasource without any connection pooling. 2) Datasource with pooled
* connections. 3) Datasource with pooled connection and transaction capabilities.
*
* The invokeAllMethods was setting only some specific properties Modified the code so that any key
* value mentioned in <property>tag is attempted for setting. If the property has a key as
* serverName , then the setter method is invoked with the name setServerName & the value present in
* value attribute is passed Made the Exception handling robust
*
* Changed invokeAllMethods wrt the change in cache.xml for vendor specific properties.
*
*/
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ManagedConnectionFactory;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import org.apache.logging.log4j.Logger;
import org.apache.geode.internal.ClassPathLoader;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.logging.log4j.LocalizedMessage;
import org.apache.geode.internal.util.PasswordUtil;
public class DataSourceFactory {
private static final Logger logger = LogService.getLogger();
/** Creates a new instance of DataSourceFactory */
public DataSourceFactory() {}
/**
* This function returns the Basic datasource without any pooling.
*
* @param configMap a map containing configurations required for datasource.
* @throws DataSourceCreateException
* @return ??
*/
public static DataSource getSimpleDataSource(Map configMap, List props)
throws DataSourceCreateException {
ConfiguredDataSourceProperties configs = createDataSourceProperties(configMap);
if (configs.getJDBCDriver() == null) {
logger.error(LocalizedMessage.create(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETSIMPLEDATASOURCEJDBC_DRIVER_IS_NOT_AVAILABLE));
throw new DataSourceCreateException(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETSIMPLEDATASOURCEJDBC_DRIVER_IS_NOT_AVAILABLE
.toLocalizedString());
}
if (configs.getURL() == null) {
logger.error(LocalizedMessage.create(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETSIMPLEDATASOURCEURL_STRING_TO_DATABASE_IS_NULL));
throw new DataSourceCreateException(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETSIMPLEDATASOURCEURL_STRING_TO_DATABASE_IS_NULL
.toLocalizedString());
}
try {
return new GemFireBasicDataSource(configs);
} catch (Exception ex) {
logger.error(LocalizedMessage.create(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORY_GETSIMPLEDATASOURCE_EXCEPTION_WHILE_CREATING_GEMFIREBASICDATASOURCE_EXCEPTION_STRING_0,
ex.getLocalizedMessage()), ex);
throw new DataSourceCreateException(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORY_GETSIMPLEDATASOURCE_EXCEPTION_WHILE_CREATING_GEMFIREBASICDATASOURCE_EXCEPTION_STRING_0
.toLocalizedString(ex.getLocalizedMessage()),
ex);
}
}
/**
* This function creats a data dource from the ManagedConnectionFactory class using the
* connectionManager.
*
* @param configMap
* @param props
* @return javax.sql.DataSource
* @throws DataSourceCreateException
*/
public static ClientConnectionFactoryWrapper getManagedDataSource(Map configMap, List props)
throws DataSourceCreateException {
Object cf = null;
ManagedConnectionFactory mcf = null;
ConfiguredDataSourceProperties configs = createDataSourceProperties(configMap);
if (configs.getMCFClass() == null) {
logger.error(LocalizedMessage.create(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETMANAGEDDATASOURCEMANAGED_CONNECTION_FACTORY_CLASS_IS_NOT_AVAILABLE));
throw new DataSourceCreateException(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETMANAGEDDATASOURCEMANAGED_CONNECTION_FACTORY_CLASS_IS_NOT_AVAILABLE
.toLocalizedString());
} else {
try {
Class cl = ClassPathLoader.getLatest().forName(configs.getMCFClass());
mcf = (ManagedConnectionFactory) cl.newInstance();
invokeAllMethods(cl, mcf, props);
} catch (Exception ex) {
logger.error(LocalizedMessage.create(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETMANAGEDDATASOURCE_EXCEPTION_IN_CREATING_MANAGED_CONNECTION_FACTORY_EXCEPTION_STRING_0,
ex), null);
throw new DataSourceCreateException(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETMANAGEDDATASOURCE_EXCEPTION_IN_CREATING_MANAGED_CONNECTION_FACTORY_EXCEPTION_STRING_0
.toLocalizedString(ex));
}
}
// Initialize the managed connection factory class
// Instantiate a connection manager with the managed-conn-facory-class
// and generate a connection pool.
Object cm = null;
if (configs.getMCFClass().equals("org.apache.persistence.connection.internal.ConnFactory")) {
cm = new FacetsJCAConnectionManagerImpl(mcf, configs);
} else {
cm = new JCAConnectionManagerImpl(mcf, configs);
}
try {
cf = mcf.createConnectionFactory((ConnectionManager) cm);
} catch (Exception ex) {
logger.error(LocalizedMessage.create(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETMANAGEDDATASOURCE_EXCEPTION_IN_CREATING_MANAGED_CONNECTION_FACTORY_EXCEPTION_STRING_0,
ex), null);
throw new DataSourceCreateException(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETMANAGEDDATASOURCE_EXCEPTION_IN_CREATING_MANAGED_CONNECTION_FACTORY_EXCEPTION_STRING_0
.toLocalizedString(ex));
}
return new ClientConnectionFactoryWrapper(cf, cm);
}
/**
* This function returns the datasource with connection pooling.
*
* @param configMap a map containing configurations required for datasource.
* @throws DataSourceCreateException
* @return ???
*/
public static DataSource getPooledDataSource(Map configMap, List props)
throws DataSourceCreateException {
ConfiguredDataSourceProperties configs = createDataSourceProperties(configMap);
String connpoolClassName = configs.getConnectionPoolDSClass();
if (connpoolClassName == null) {
logger.error(LocalizedMessage.create(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETPOOLEDDATASOURCECONNECTIONPOOLDATASOURCE_CLASS_NAME_FOR_THE_RESOURCEMANAGER_IS_NOT_AVAILABLE));
throw new DataSourceCreateException(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETPOOLEDDATASOURCECONNECTIONPOOLDATASOURCE_CLASS_NAME_FOR_THE_RESOURCEMANAGER_IS_NOT_AVAILABLE
.toLocalizedString());
}
try {
Class cl = ClassPathLoader.getLatest().forName(connpoolClassName);
Object Obj = cl.newInstance();
invokeAllMethods(cl, Obj, props);
return new GemFireConnPooledDataSource((ConnectionPoolDataSource) Obj, configs);
} catch (Exception ex) {
String exception =
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORY_GETPOOLEDDATASOURCE_EXCEPTION_CREATING_CONNECTIONPOOLDATASOURCE_EXCEPTION_STRING_0
.toLocalizedString(new Object[] {ex});
logger.error(LocalizedMessage.create(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORY_GETPOOLEDDATASOURCE_EXCEPTION_CREATING_CONNECTIONPOOLDATASOURCE_EXCEPTION_STRING_0,
ex), ex);
throw new DataSourceCreateException(exception, ex);
}
}
/**
* This function returns the datasource with connection pooling and transaction participation
* capabilities.
*
* @param configMap a map containing configurations required for datasource.
* @throws DataSourceCreateException
* @return ???
*/
public static DataSource getTranxDataSource(Map configMap, List props)
throws DataSourceCreateException {
ConfiguredDataSourceProperties configs = createDataSourceProperties(configMap);
String xaClassName = configs.getXADSClass();
if (xaClassName == null) {
logger.error(LocalizedMessage.create(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETTRANXDATASOURCEXADATASOURCE_CLASS_NAME_FOR_THE_RESOURCEMANAGER_IS_NOT_AVAILABLE));
throw new DataSourceCreateException(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORYGETTRANXDATASOURCEXADATASOURCE_CLASS_NAME_FOR_THE_RESOURCEMANAGER_IS_NOT_AVAILABLE
.toLocalizedString());
}
if (TEST_CONNECTION_HOST != null) {
props.add(new ConfigProperty("serverName", TEST_CONNECTION_HOST, "java.lang.String"));
}
if (TEST_CONNECTION_PORT != null) {
props.add(new ConfigProperty("portNumber", TEST_CONNECTION_PORT, "int"));
}
try {
Class cl = ClassPathLoader.getLatest().forName(xaClassName);
Object Obj = cl.newInstance();
invokeAllMethods(cl, Obj, props);
return new GemFireTransactionDataSource((XADataSource) Obj, configs);
} catch (Exception ex) {
String exception =
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORY_GETTRANXDATASOURCE_EXCEPTION_IN_CREATING_GEMFIRETRANSACTIONDATASOURCE__EXCEPTION_STRING_0
.toLocalizedString(new Object[] {ex});
logger.error(LocalizedMessage.create(
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORY_GETTRANXDATASOURCE_EXCEPTION_IN_CREATING_GEMFIRETRANSACTIONDATASOURCE__EXCEPTION_STRING_0,
ex), ex);
throw new DataSourceCreateException(exception, ex);
}
}
/**
* Creates ConfiguredDataSourceProperties from map.
*
* @param configMap a map containing configurations required for datasource.
* @return ConfiguredDataSourceProperties
*/
private static ConfiguredDataSourceProperties createDataSourceProperties(Map configMap) {
ConfiguredDataSourceProperties configs = new ConfiguredDataSourceProperties();
Iterator entries = configMap.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
String name = (String) entry.getKey();
final Object obj = entry.getValue();
if (name.equals("connection-url"))
configs.setURL((String) obj);
else if (name.equals("user-name"))
configs.setUser((String) obj);
else if (name.equals("password"))
configs.setPassword(PasswordUtil.decrypt((String) obj));
else if (name.equals("jdbc-driver-class"))
configs.setJDBCDriver((String) obj);
else if (name.equals("init-pool-size"))
configs.setInitialPoolSize(Integer.parseInt((String) (obj == null
? String.valueOf(DataSourceResources.CONNECTION_POOL_DEFAULT_INIT_LIMIT) : obj)));
else if (name.equals("max-pool-size"))
configs.setMaxPoolSize(Integer.parseInt((String) (obj == null
? String.valueOf(DataSourceResources.CONNECTION_POOL_DEFAULT_MAX_LIMIT) : obj)));
else if (name.equals("idle-timeout-seconds"))
configs.setConnectionExpirationTime(Integer.parseInt((String) (obj == null
? String.valueOf(DataSourceResources.CONNECTION_POOL_DEFAULT_EXPIRATION_TIME) : obj)));
else if (name.equals("blocking-timeout-seconds"))
configs.setConnectionTimeOut(Integer.parseInt((String) (obj == null
? String.valueOf(DataSourceResources.CONNECTION_POOL_DEFAULT_ACTIVE_TIME_OUT) : obj)));
else if (name.equals("login-timeout-seconds"))
configs.setLoginTimeOut(Integer.parseInt((String) (obj == null
? String.valueOf(DataSourceResources.CONNECTION_POOL_DEFAULT_CLIENT_TIME_OUT) : obj)));
else if (name.equals("conn-pooled-datasource-class"))
configs.setConnectionPoolDSClass((String) obj);
else if (name.equals("xa-datasource-class"))
configs.setXADSClass((String) obj);
else if (name.equals("managed-conn-factory-class"))
configs.setMCFClass((String) obj);
else if (name.equals("transaction-type"))
configs.setTransactionType((String) obj);
}
/*
* Test hook for replacing URL
*/
if (TEST_CONNECTION_URL != null) {
configs.setURL((String) TEST_CONNECTION_URL);
}
return configs;
}
/*
* Test hook for replacing URL
*/
private static String TEST_CONNECTION_URL = null;
private static String TEST_CONNECTION_HOST = null;
private static String TEST_CONNECTION_PORT = null;
/*
* Test hook for replacing URL
*/
public static void setTestConnectionUrl(String url) {
TEST_CONNECTION_URL = url;
}
/*
* Test hook for replacing Host
*/
public static void setTestConnectionHost(String host) {
TEST_CONNECTION_HOST = host;
}
/*
* Test hook for replacing Port
*/
public static void setTestConnectionPort(String port) {
TEST_CONNECTION_PORT = port;
}
/**
*
* dynamically invokes all the methods in the specified object.
*
* Asif: Rewrote the function as the number of properties to be set for a given datasource is
* dynamic
*
* Rohit: Rewrote to accomodate the change in cache.xml. property tag is now replaced by
* config-property for vendor specific data to get the Class name dynamically. CLass name is
* specified in config-property-type tag and is stored in ConfigProperty Data Object after parsing
* cache.xml.
*
* @param c class of the object
* @param cpdsObj The object
*/
private static void invokeAllMethods(Class c, Object cpdsObj, List props)
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
String key = null;
String value = null;
String type = null;
String methodName = null;
Method m = null;
for (Iterator it = props.iterator(); it.hasNext();) {
ConfigProperty cp = (ConfigProperty) it.next();
key = cp.getName();
value = cp.getValue();
type = cp.getType();
if (key.indexOf("password") != -1) {
value = PasswordUtil.decrypt(String.valueOf(value));
}
methodName = new StringBuffer("set").append(Character.toUpperCase(key.charAt(0)))
.append(key.length() > 1 ? key.substring(1) : "").toString();
try {
Class cl = null;
Class realClass = null;
if ("int".equals(type)) {
cl = int.class;
realClass = java.lang.Integer.class;
} else {
cl = ClassPathLoader.getLatest().forName(type);
realClass = cl;
}
Constructor cr = realClass.getConstructor(new Class[] {java.lang.String.class});
Object ob = cr.newInstance(new Object[] {value});
m = c.getMethod(methodName, new Class[] {cl});
m.invoke(cpdsObj, new Object[] {ob});
} catch (ClassNotFoundException ex) {
String exception =
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORY_INVOKEALLMETHODS_EXCEPTION_IN_CREATING_CLASS_WITH_THE_GIVEN_CONFIGPROPERTYTYPE_CLASSNAME_EXCEPTION_STRING_0
.toLocalizedString(ex.toString());
if (logger.isDebugEnabled()) {
logger.debug(exception, ex);
}
} catch (NoSuchMethodException ex) {
String exception =
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORY_INVOKEALLMETHODS_EXCEPTION_IN_CREATING_METHOD_USING_CONFIGPROPERTYNAME_PROPERTY_EXCEPTION_STRING_0
.toLocalizedString(ex.toString());
if (logger.isDebugEnabled()) {
logger.debug(exception, ex);
}
} catch (InstantiationException ex) {
String exception =
LocalizedStrings.DataSourceFactory_DATASOURCEFACTORY_INVOKEALLMETHODS_EXCEPTION_IN_CREATING_INSTANCE_OF_THE_CLASS_USING_THE_CONSTRUCTOR_WITH_A_STRING_PARAMETER_EXCEPTION_STRING_0
.toLocalizedString(ex.toString());
if (logger.isDebugEnabled()) {
logger.debug(exception, ex);
}
}
}
}
}